<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
  <script src="../lib/three/three.js"></script>
  <script src="../lib/three/tween.min.js"></script>
  <script src="../lib/three/dat.gui.js"></script>
</head>

<body>

</body>

</html>

<script>
  const clock = new THREE.Clock()
  // 创建一个场景
  const scene = new THREE.Scene();

  // 创建一个相机 视点
  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
  // 设置相机的位置
  camera.position.set(0, 30, 100);
  camera.lookAt(new THREE.Vector3(0, 0, 0));

  // 创建一个渲染器
  const renderer = new THREE.WebGLRenderer();
  renderer.setClearColor(0x000000);
  // 设置渲染器尺寸
  renderer.setSize(window.innerWidth, window.innerHeight);

  document.body.appendChild(renderer.domElement);

  // 添加灯光
  const spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(2000, 8000, 4000);
  scene.add(spotLight);

  function getSprite() {
    const canvas = document.createElement('canvas')
    const size = 8
    canvas.width = size * 2
    canvas.height = size * 2

    const ctx = canvas.getContext('2d')

    const gradient = ctx.createLinearGradient(size, size, 0, size, size, size)
    gradient.addColorStop(0.1, 'rgba(0, 255, 255, 1)')

    ctx.fillStyle = gradient;
    ctx.arc(size, size, size / 2, 0, Math.PI * 2)
    ctx.fill()

    const texture = new THREE.Texture(canvas)
    texture.needsUpdate = true
    return texture
  }

  // 创建一个立方体
  const geometry = new THREE.BoxGeometry(10, 10, 10, 10, 10, 10);

  // 存储原始坐标
  const indexList = []

  // 设定当前随机的范围
  const range = 100

  const controls = {
    // 是否为立方体
    polymeric: false,
    // 组合后是否为立方体
    completeMesh: false,
    // 是否现在显示立方体
    showMesh: false
  }

  function createRandomPosition(i) {
    geometry.vertices[i].x += Math.random() * range - range / 2
    geometry.vertices[i].y += Math.random() * range - range / 2
    geometry.vertices[i].z += Math.random() * range - range / 2
  }

  let cloud;

  function createMesh() {
    cloud = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10, 10, 10, 10), new THREE.MeshNormalMaterial());
    scene.add(cloud)
  }

  function createPointCloud() {
    let listen = false;
    for (let i = 0; i < geometry.vertices.length; i++) {
      indexList.push({
        x: geometry.vertices[i].x,
        y: geometry.vertices[i].y,
        z: geometry.vertices[i].z
      })
      createRandomPosition(i);

      if (controls.polymeric) {
        const tween = new TWEEN.Tween(geometry.vertices[i]).to(indexList[i], 2000).start()
        if (!listen) {
          listen = true

          if (controls.completeMesh) {
            tween.onComplete(() => {
              scene.remove(cloud)
              createMesh()
            })
          }
        }
      }
    }

    const material = new THREE.PointCloudMaterial({
      size: 2,
      transparent: true,
      map: getSprite()
    })

    cloud = new THREE.PointCloud(geometry, material)
    cloud.sortParticles = true

    scene.add(cloud)
  }

  createPointCloud()

  const gui = new dat.GUI()
  const onChange = () => {
    scene.remove(cloud)
    controls.showMesh ? createMesh() : createPointCloud();
  }
  for (const key in controls) {
    gui.add(controls, key).onChange(onChange)
  }

  const animation = () => {
    scene.rotation.y += 0.01
    // 渲染
    renderer.render(scene, camera);
    TWEEN.update()

    requestAnimationFrame(animation);
  }
  animation()
</script>